#include "quickjs-libc.h"
#include "quickjs.h"
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <limits.h>
#include <unistd.h>

static JSValue setTimeout(JSContext *ctx, JSValueConst thiz, int argc,
                          JSValueConst *argv) {
  if (argc < 2) {
    return JS_ThrowTypeError(ctx, "Expected at least 2 arguments");
  }
  JSValue callback = argv[0];

  int32_t delay;
  int ret;
  ret = JS_ToInt32(ctx, &delay, argv[1]);
  // if (ret <= 0) {
  //   return JS_ThrowTypeError(ctx, "parse arguments 2 to int32 failed!");
  // }
  sleep(delay / 1000);
  JS_Call(ctx, callback, JS_UNDEFINED, 0, nullptr);
  return JS_UNDEFINED;
}
static JSValue consoleLog(JSContext *ctx, JSValueConst thiz, int argc,
                          JSValueConst *argv) {
  int i = 0;
  std::cout << "[console]";
  for (; i < argc; i++) {
    JSValueConst param = argv[i];
    std::cout << JS_ToCString(ctx, param);
  }
  std::cout << std::endl;
}

int main(int argc, char **argv) {
  JSRuntime *rt;
  JSContext *ctx;
  JSValue global_obj;

  rt = JS_NewRuntime();
  ctx = JS_NewContext(rt);
  global_obj = JS_GetGlobalObject(ctx);

  char project_dir[PATH_MAX];
  getcwd(project_dir, PATH_MAX);

  std::string filename =
      std::string(project_dir) + std::string("/../../../../src/asyncer.js");
  std::cout << "projectDir=" << project_dir << std::endl
            << "filename=" << filename << std::endl;
  size_t buf_len;
  char *buf;
  buf = (char *)js_load_file(ctx, &buf_len, filename.c_str());
  if (!buf) {
    std::cout << "load file failed!" << std::endl;
    JS_FreeValue(ctx, global_obj);
    JS_FreeContext(ctx);
    JS_FreeRuntime(rt);
    return 1;
  }

  JSValue consoleLogFunc = JS_NewCFunction(ctx, consoleLog, "consoleLog", 1);
  JS_SetPropertyStr(ctx, global_obj, "consoleLog", consoleLogFunc);

  JSValue setTimeoutFunc = JS_NewCFunction(ctx, setTimeout, "setTimeout", 1);
  JS_SetPropertyStr(ctx, global_obj, "setTimeout", setTimeoutFunc);

  JSValue ret = JS_Eval(ctx, buf, buf_len, filename.c_str(), 0);
  if (JS_IsException(ret)) {
    JS_FreeValue(ctx, global_obj);
    JS_FreeContext(ctx);
    JS_FreeRuntime(rt);
    return 1;
  }

  JSValue test1Func = JS_GetPropertyStr(ctx, global_obj, "test1");
  JSValue test1Ret = JS_Call(ctx, test1Func, global_obj, 0, NULL);

  std::cout << "eval: " << JS_ToCString(ctx, test1Ret)
            << " is JobPending: " << JS_IsJobPending(rt) << std::endl;

  for (;;) {
    JSContext *ctx1;
    int lret = JS_ExecutePendingJob(rt, &ctx1);
    if (lret <= 0) {
      JSPromiseStateEnum state = JS_PromiseState(ctx, test1Ret);

      std::cout << "pendingJob empty!"
                << "promise State: ==fulfilled "
                << (state == JS_PROMISE_FULFILLED) << std::endl;
      // js_std_dump_error(ctx);
      break;
    }
    // JSPromiseStateEnum state = JS_PromiseState(ctx, test1Ret);
    // if (state != JS_PROMISE_PENDING) {
    //   std::cout << "un pending!" << std::endl;
    //   break;
    // }
  }
  JSValue result = JS_PromiseResult(ctx, test1Ret);
  std::cout << "resolved: !" << JS_ToCString(ctx, result) << std::endl;

  return 0;
}